package org.wheel.jpa.configs;

import freemarker.template.Template;
import org.hibernate.SessionFactory;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.wheel.base.stmt.OrmSqlUtils;
import org.wheel.base.utils.ReflectionUtils;
import org.wheel.jpa.factory.BaseRepositoryFactoryBean;
import org.wheel.jpa.provider.JpaHibernatePersistenceProvider;
import org.wheel.jpa.repository.BaseRepository;

import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.Map;

/**
 *
 * @author klaus.wang
 *
 */
@Configuration
@EnableTransactionManagement
@Import({JpaHibernatePersistenceProvider.class})
@EnableJpaRepositories(repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class, basePackages = JpaRepositoryConfig.jpaBasePackages)
public class JpaRepositoryConfig extends HibernatePersistenceProvider implements InitializingBean, HibernatePropertiesCustomizer {

	@Value(jpaBasePackages)
	private String basePackages;

	private Template baseTemplate;

	private freemarker.template.Configuration configuration;

	private static final Logger logger = LoggerFactory.getLogger(JpaRepositoryConfig.class);

	public static final String jpaBasePackages = "${wheel.jpa.basePackages:org.wheel.**.repositories}";

	@Bean
	public HibernateTemplate hibernateTemplate(EntityManagerFactory entityManagerFactory) {
		return new HibernateTemplate(entityManagerFactory.unwrap(SessionFactory.class));
	}

	@Override
	public void afterPropertiesSet() throws Exception {

		this.baseTemplate = configuration.getTemplate("/jpa/base-jpa.ftl", "UTF-8");

		ReflectionUtils.scanPackages(basePackages, "/*Repository.xml", (mrf, resource) -> {
			try {
				OrmSqlUtils.add(resource.getInputStream());
			} catch (Exception e) {
				return false;
			}
			return true;
		});

		ReflectionUtils.scanPackages(basePackages, (cls) -> {
			Class<?>[] interfaces = cls.getInterfaces();
			if (interfaces.length == 0) {
				return true;
			}
			if (Arrays.asList(interfaces).contains(BaseRepository.class)) {
				Map<String, Object> dataModel = ReflectionUtils.getClassDataModel(ReflectionUtils.getType(cls, 0));
				dataModel.put("ns", cls.getName());
				this.addClass(dataModel);
			}
			return true;
		});

	}

	/**
	 * 创建基础模型仓库
	 */
	void addClass(Map<String, Object> dataModel) {
		if (dataModel == null) {
			return;
		}
		InputStream is;
		try (Writer out = new StringWriter()) {
			baseTemplate.process(dataModel, out);
			is = new ByteArrayInputStream(out.toString().getBytes());
			OrmSqlUtils.add(is);
			is.close();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 必须实现这个接口
	 */
	@Override
	public void customize(Map<String, Object> hibernateProperties) {
		if (!hibernateProperties.containsKey(AvailableSettings.IDENTIFIER_GENERATOR_STRATEGY_PROVIDER)) {
			hibernateProperties.put(AvailableSettings.IDENTIFIER_GENERATOR_STRATEGY_PROVIDER
					, "org.wheel.jpa.boot.internal.WebIdentifierGeneratorStrategyProvider");
		}
	}

	@Resource
	public void setConfiguration(freemarker.template.Configuration configuration) {
		this.configuration = configuration;
	}
}
